/*
 * Copyright 2022-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.instancio.test.features.assign;

import org.instancio.Assign;
import org.instancio.Assignment;
import org.instancio.Instancio;
import org.instancio.Model;
import org.instancio.junit.InstancioExtension;
import org.instancio.test.support.pojo.misc.StringsAbc;
import org.instancio.test.support.pojo.misc.StringsDef;
import org.instancio.test.support.pojo.misc.StringsGhi;
import org.instancio.test.support.tags.Feature;
import org.instancio.test.support.tags.FeatureTag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.instancio.Select.field;

/**
 * Use case: set destination values based on values generated by {@code emit()}.
 */
@FeatureTag({Feature.ASSIGN, Feature.MODEL})
@ExtendWith(InstancioExtension.class)
class AssignMultipleOriginsTest {

    private final Model<List<StringsAbc>> model = Instancio.ofList(StringsAbc.class)
            .size(6)
            .generate(field(StringsDef::getE), gen -> gen.emit()
                    .item("E1", 1)
                    .item("E2", 2)
                    .item("E3", 3))
            .toModel();

    @Test
    void givenE_setFH() {
        final Assignment[] assignments = {
                Assign.given(StringsDef::getE).is("E1")
                        .set(field(StringsDef::getF), "F1")
                        .set(field(StringsGhi::getH), "H1"),

                Assign.given(StringsDef::getE).is("E2")
                        .set(field(StringsDef::getF), "F2")
                        .set(field(StringsGhi::getH), "H2"),

                Assign.given(StringsDef::getE).is("E3")
                        .set(field(StringsDef::getF), "F3")
                        .set(field(StringsGhi::getH), "H3")
        };

        final List<StringsAbc> results = Instancio.of(model)
                .assign(assignments)
                .create();

        assertEFH(results.get(0), 1);
        assertEFH(results.get(1), 2);
        assertEFH(results.get(2), 2);
        assertEFH(results.get(3), 3);
        assertEFH(results.get(4), 3);
        assertEFH(results.get(5), 3);
    }

    private static void assertEFH(final StringsAbc result, final int expectedNumber) {
        assertThat(result.def.e).isEqualTo("E" + expectedNumber);
        assertThat(result.def.f).isEqualTo("F" + expectedNumber);
        assertThat(result.def.ghi.h).isEqualTo("H" + expectedNumber);
    }

    @Test
    void givenE_setAD() {
        final Assignment[] assignments = {
                Assign.given(StringsDef::getE).is("E1")
                        .set(field(StringsAbc::getA), "A1")
                        .set(field(StringsDef::getD), "D1"),

                Assign.given(StringsDef::getE).is("E2")
                        .set(field(StringsAbc::getA), "A2")
                        .set(field(StringsDef::getD), "D2"),

                Assign.given(StringsDef::getE).is("E3")
                        .set(field(StringsAbc::getA), "A3")
                        .set(field(StringsDef::getD), "D3")
        };

        final List<StringsAbc> results = Instancio.of(model)
                .assign(assignments)
                .create();

        assertADE(results.get(0), 1);
        assertADE(results.get(1), 2);
        assertADE(results.get(2), 2);
        assertADE(results.get(3), 3);
        assertADE(results.get(4), 3);
        assertADE(results.get(5), 3);
    }

    private static void assertADE(final StringsAbc result, final int expectedNumber) {
        assertThat(result.a).isEqualTo("A" + expectedNumber);
        assertThat(result.def.d).isEqualTo("D" + expectedNumber);
        assertThat(result.def.e).isEqualTo("E" + expectedNumber);
    }

}
